﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using KPIS.GERP.Models;
using KPIS.GERP.Models.Class;
using KPIS.GERP.Models.HRM;
using System.Data.Entity;

namespace KPIS.GERP.WCF.GHRMS
{
    // NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "TimeAttentsService" in code, svc and config file together.
    // NOTE: In order to launch WCF Test Client for testing this service, please select TimeAttentsService.svc or TimeAttentsService.svc.cs at the Solution Explorer and start debugging.
    [ServiceContract]
    public interface ITimeAttentsService
    {
        [OperationContract]
        string CalculateTimeAttendanceMonthly(int bgYear, int? month, int? jobSiteId, int? empTypeId, int clickCalculateBy);

        [OperationContract]
        void AutoAssignTimeAttendanceDaily(int bgYear, int month, int clickCalculateBy);

        [OperationContract]
        IEnumerable<PsTimeAttSummary> ListTimeAttendanceSummaries();

        [OperationContract]
        IEnumerable<PsTimeAttendants> ListPsTimeAttendances();

        [OperationContract]
        IEnumerable<PsTimeAttendanceMonthly> ListPsTimeAttendanceMonthlies();

        [OperationContract]
        IEnumerable<PsTimeAttCalAudit> ListPsTimeAttCalAudit();
        [OperationContract]
        IEnumerable<PsAbsenceType> SelectListAbsenceType();
        [OperationContract]
        void InsertPsTimeAttendanceMonthly(PsTimeAttendanceMonthly model);
        [OperationContract]
        void UpdatePsTimeAttendanceMonthly(PsTimeAttendanceMonthly model);
        [OperationContract]
        void DeletePsTimeAttendanceMonthly(int seq, int empSeq);
    }

    public class TimeAttentsService : ITimeAttentsService
    {
        private readonly AbsenceUtility AbsenceUtility = new AbsenceUtility();

        public string CalculateTimeAttendanceMonthly(int year, int? month, int? jobSiteId, int? empTypeId, int clickCalculateBy)
        {
            GERPContext db = new GERPContext(true);
            string result = "การคำนวนเริ่มต้น";
            var audit = new PsTimeAttCalAudit(year, month, jobSiteId, empTypeId, clickCalculateBy, false, result);
            db.PsTimeAttCalAudits.Add(audit);
            db.SaveChanges();

            try
            {
                DateTime firstDate, lastDate;
                if (month.HasValue)
                {
                    firstDate = new DateTime(year, month.Value, 1);
                    lastDate = new DateTime(year, month.Value, DateTime.DaysInMonth(year, month.Value));
                }
                else
                {
                    // year mean bg year = 2557 = > 1 Oct 2557 to 30 Nov 2558
                    firstDate = new DateTime(year - 1, 10, 1);
                    lastDate = new DateTime(year, 9, DateTime.DaysInMonth(year, 9));
                }

                audit.Remarks = "เริ่มดึงข้อมูลพื้นฐาน";
                db.SaveChanges();

                var queryEmployees = db.Employees.Where(o => o.RecordStatus != "D").ToList();
                queryEmployees = queryEmployees.Where(o => !o.WorkStartDate.HasValue || o.WorkStartDate.Value.Date <= firstDate.Date).ToList();
                List<Employee> employeesWithinDateRange = queryEmployees.OrderBy(o => o.EmpId).ToList();
                int?[] employeesWithinDateRangeIds = employeesWithinDateRange.Select(o => (int?) o.EmpId).ToArray();

                List<PsTimeAttendants> allTAs = db.PsTimeAttendants.Where(o => o.RecordStatus != "D").ToList();
                List<PsTimeAttSummary> tasAll = db.PsTimeAttendanceSummaries.ToList();
                List<PsAbsence> allApvAbs = db.PsAbsences.Where(o => o.RecordStatus != "D").ToList();

                TimeSpan startWorkingTime = HrmSystemControlUtil.StartWorking,
                    endWorkingTime = HrmSystemControlUtil.EndWorking,
                    startWorkingTimeAfternoon = HrmSystemControlUtil.StartWorkingAfternoon,
                    endWorkingTimeMorning = HrmSystemControlUtil.EndWorkingMorning;
                List<DateTime> dates = AbsenceUtility.GetRegularWorkingDayList(firstDate, lastDate).ToList();

                audit.Remarks = "เตรียมข้อมูลสรุปการเข้างานในทุกวันของเดือน";
                db.SaveChanges();

                // pre-generate working status of all employee and all date and time
                int i = 0;
                int iCount = employeesWithinDateRange.Count;
                foreach (var employee in employeesWithinDateRange)
                {
                    audit.Remarks = string.Format("เตรียมข้อมูลสรุปการเข้างานของพนักงานในทุกวันของเดือน  {0}/{1}", ++i, iCount);
                    db.SaveChanges();
                    foreach (var dateTime in dates)
                    {
                        // Delete old summary
                        var tas1 = tasAll.Where(o => o.EmpId == employee.EmpId && o.CalForDate == dateTime.Date);

                        var obj1 = allTAs.FirstOrDefault(o => o.EmpId == employee.EmpId && o.WorkingDate.GetValueOrDefault().Date == dateTime.Date);
                        if (tas1.Count() > 1)
                        {
                            foreach (var ta1 in tas1.Reverse().Skip(1))
                            {
                                db.PsTimeAttendanceSummaries.Remove(ta1);
                            }
                            var tas2 = tas1.Reverse().First();
                            tas2.UpdateInfo(obj1, dateTime, employee.EmpId,
                                startWorkingTime, endWorkingTime, startWorkingTimeAfternoon, endWorkingTimeMorning);
                            tas2.UpdateAttenStatus(allApvAbs);
                            tas2.UpdatedBy = clickCalculateBy;
                            tas2.UpdatedWhen = DateTime.Now;
                        }
                        else if (tas1.Count() == 1)
                        {
                            var tas2 = tas1.First();
                            tas2.UpdateInfo(obj1, dateTime, employee.EmpId,
                                startWorkingTime, endWorkingTime, startWorkingTimeAfternoon, endWorkingTimeMorning);
                            tas2.UpdateAttenStatus(allApvAbs);
                            tas2.UpdatedBy = clickCalculateBy;
                            tas2.UpdatedWhen = DateTime.Now;
                        }
                        else
                        {
                            var tas2 = new PsTimeAttSummary(obj1, dateTime, employee.EmpId,
                                startWorkingTime, endWorkingTime, startWorkingTimeAfternoon, endWorkingTimeMorning);

                            db.PsTimeAttendanceSummaries.Add(tas2);
                        }

                    }
                }

                db.SaveChanges();

                tasAll = db.PsTimeAttendanceSummaries
                    .Where(o => o.CalForDate >= firstDate &&
                                o.CalForDate <= lastDate &&
                                employeesWithinDateRangeIds.Contains(o.EmpId)).ToList();

                audit.Remarks += ", ดึงสรุปรายวันไปไว้ที่สรุปรายเดือน";
                db.SaveChanges();
                foreach (var employee in employeesWithinDateRange)
                {

                    // Update monthly
                    PsTimeAttendanceMonthly tam;
                    var tams = db.PsTimeAttendanceMonthlies.Where(o => o.EmpId == employee.EmpId
                                                                       && o.RecordStatus != "D"
                                                                       && o.WorkingYear == year
                                                                       && o.WorkingMonth == month);

                    if (tams.Count() > 1)
                    {
                        foreach (var eachTam in tams.Reverse().Skip(1))
                        {
                            eachTam.RecordStatus = "D";
                        }
                        tam = tams.Reverse().First();
                    }
                    else if (tams.Count() == 1)
                    {
                        tam = tams.First();
                    }
                    else
                    {
                        tam = new PsTimeAttendanceMonthly
                        {
                            EmpId = employee.EmpId,
                            WorkingMonth = month,
                            WorkingYear = year,
                            RecordStatus = "U",
                            CreatedBy = 1,
                            CreatedWhen = DateTime.Now
                        };
                    }


                    var tasOfHim = tasAll.Where(o => o.EmpId == employee.EmpId &&
                                                     o.CalForDate.GetValueOrDefault().Date.Month == month &&
                                                     o.CalForDate.GetValueOrDefault().Date.Year == year).ToList();

                    decimal validAbsenceDayCount = Convert.ToDecimal(tasOfHim.Count(o => o.CalAbsenceId.HasValue)*0.5 +
                                                                     tasOfHim.Count(o => o.CalAbsenceAfternoonId.HasValue)*0.5);

                    //decimal invalidAbsenceDayCount = allApvAbs.Where(o => o.EmpId == employee.EmpId &&
                    //                                                      (o.StartDate <= firstDate || o.EndDate >= lastDate));

                    decimal lateDayCount = tasOfHim.Count(o => o.CalIsLate == true);

                    tam.PlanWorkingDayCount = tasOfHim.Count();
                    tam.ActualWorkingDayCount = tasOfHim.Count(o => o.CalIsAtten);
                    tam.ValidAbsenceDayCount = validAbsenceDayCount;
                    tam.InvalidAbsenceDayCount = 0;
                    tam.LateDayCount = lateDayCount;
                    tam.CalNotWorkingDayCount();
                    tam.UpdatedBy = 1;
                    tam.UpdatedWhen = DateTime.Now;

                    if (tam.TimeAttMonthId != 0)
                    {

                    }
                    else
                    {
                        db.PsTimeAttendanceMonthlies.Add(tam);
                    }
                }

                result = "การคำนวนสำเร็จ";
                audit.Remarks = result;
                audit.Success = true;
                db.SaveChanges();
            }
            catch (Exception ex)
            {
                audit.Remarks = "การคำนวนไม่สำเร็จ [" + ex.Message + "]";
                db.SaveChanges();
            }
            return result; 
        }

        public void AutoAssignTimeAttendanceDaily(int year, int month, int clickCalculateBy)
        {
            GERPContext db = new GERPContext();
            DateTime firstDate, lastDate;
            firstDate = new DateTime(year, month, 1);
            lastDate = new DateTime(year, month, DateTime.DaysInMonth(year, month));
            
            var queryEmployees = db.Employees.Where(o => o.RecordStatus != "D").ToList();
            queryEmployees = queryEmployees.Where(o => !o.WorkStartDate.HasValue || o.WorkStartDate.Value.Date <= firstDate.Date).ToList();
            List<Employee> employeesWithinDateRange = queryEmployees.OrderBy(o => o.EmpId).ToList();
            
            List<PsTimeAttendants> allTAs = db.PsTimeAttendants.Where(o => o.RecordStatus != "D").ToList();
            List<DateTime> dates = AbsenceUtility.GetRegularWorkingDayList(firstDate, lastDate).ToList();
            
            TimeSpan startWorking = HrmSystemControlUtil.StartWorking;
            TimeSpan endWorking = HrmSystemControlUtil.EndWorking;
            foreach (var employee in employeesWithinDateRange)
            {
                foreach (var dateTime in dates)
                {
                    // Delete old summary 
                    var obj1 = allTAs.FirstOrDefault(o => o.EmpId == employee.EmpId && o.WorkingDate.GetValueOrDefault().Date == dateTime.Date);
                    if (obj1 == null)
                    {
                        db.PsTimeAttendants.Add(
                            new PsTimeAttendants
                            {
                                WorkingDate = dateTime,
                                EmpId = employee.EmpId,
                                WorkingStart = startWorking,
                                WorkingEnd = endWorking,
                                Remarks = "",
                                RecordStatus = "U",
                                CreatedBy = clickCalculateBy,
                                CreatedWhen = DateTime.Now
                            });
                    }
                }
            }
            db.SaveChanges();
        }

        public IEnumerable<PsTimeAttSummary> ListTimeAttendanceSummaries()
        {
            var db = new GERPContext(false);
            var data = db.PsTimeAttendanceSummaries
                .Include(o => o.Employee)
                .Include(o => o.Absence)
                .Include(o => o.AbsenceAfternoon)
                .Include(o => o.PsTimeAttendance).ToList();
            return data;
        }

        public IEnumerable<PsTimeAttendants> ListPsTimeAttendances()
        {
            var db = new GERPContext(false);
            var data = db.PsTimeAttendants
                .Include(o => o.Employee).ToList();
            return data;
        }

        public IEnumerable<PsTimeAttendanceMonthly> ListPsTimeAttendanceMonthlies()
        {
            var db = new GERPContext(false);
            var data = db.PsTimeAttendanceMonthlies
                .Include(o => o.Employee).ToList();

            var dataSummary = db.PsTimeAttendanceSummaries
                .Include(o => o.Absence)
                .Include(o => o.AbsenceAfternoon).ToList();
             
            foreach (var d in data)
            {
                d.PsTimeAttSummaries.AddRange(dataSummary.Where(o => o.RecordStatus != "D" &&
                                                                     o.CalForDate >= new DateTime(d.WorkingYear.Value, d.WorkingMonth.Value, 1) &&
                                                                     o.CalForDate <= new DateTime(d.WorkingYear.Value, d.WorkingMonth.Value, DateTime.DaysInMonth(d.WorkingYear.Value, d.WorkingMonth.Value)) &&
                                                                     o.EmpId == d.EmpId
                    ));
            }

            return data;
        }

        public IEnumerable<PsTimeAttCalAudit> ListPsTimeAttCalAudit()
        {
            var db = new GERPContext(false);
            var data = db.PsTimeAttCalAudits;
            return data;
        }

        public IEnumerable<PsAbsenceType> SelectListAbsenceType()
        {
            var db = new GERPContext(false);
            var data = db.PsAbsenceTypes;
            return data;
        }

        public void InsertPsTimeAttendanceMonthly(PsTimeAttendanceMonthly model)
        {
            var db = new GERPContext(false);
            db.PsTimeAttendanceMonthlies.Add(model);
            db.SaveChanges();
        }

        public void UpdatePsTimeAttendanceMonthly(PsTimeAttendanceMonthly model)
        {
            var db = new GERPContext(false);
            db.Entry(model).State = EntityState.Modified;
            db.SaveChanges();
        }

        public void DeletePsTimeAttendanceMonthly(int seq, int empSeq)
        {
            var db = new GERPContext(true);
            var currentObject = db.PsTimeAttendanceMonthlies.Find(seq);
            currentObject.RecordStatus = "D";
            currentObject.UpdatedBy = empSeq;
            currentObject.UpdatedWhen = DateTime.Now;
            db.SaveChanges();
        }
    }
}